package com.zlyx.easy.swagger.plugins;

import static org.springframework.util.StringUtils.isEmpty;
import static springfox.documentation.swagger.common.SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER;
import static springfox.documentation.swagger.common.SwaggerPluginSupport.pluginDoesApply;
import static springfox.documentation.swagger.readers.parameter.Examples.examples;

import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import com.zlyx.easy.core.utils.StringUtils;

import io.swagger.annotations.ApiParam;
import springfox.documentation.schema.Collections;
import springfox.documentation.schema.Enums;
import springfox.documentation.schema.Example;
import springfox.documentation.service.AllowableValues;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.EnumTypeDeterminer;
import springfox.documentation.spi.service.ParameterBuilderPlugin;
import springfox.documentation.spi.service.contexts.ParameterContext;
import springfox.documentation.spring.web.DescriptionResolver;
import springfox.documentation.swagger.common.SwaggerPluginSupport;
import springfox.documentation.swagger.schema.ApiModelProperties;

@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER)
public class EasySwaggerApiParamParameterBuilder implements ParameterBuilderPlugin {
	private final DescriptionResolver descriptions;
	private final EnumTypeDeterminer enumTypeDeterminer;

	@Autowired
	protected TypeResolver typeResolver;

	@Autowired
	public EasySwaggerApiParamParameterBuilder(DescriptionResolver descriptions,
			EnumTypeDeterminer enumTypeDeterminer) {
		this.descriptions = descriptions;
		this.enumTypeDeterminer = enumTypeDeterminer;
	}

	@Override
	public void apply(ParameterContext context) {
		String name = context.resolvedMethodParameter().defaultName().get();
		Optional<ApiParam> apiParam = context.resolvedMethodParameter().findAnnotation(ApiParam.class);
		AllowableValues allowedValues = allowableValues(
				context.alternateFor(context.resolvedMethodParameter().getParameterType()),
				apiParam.map(ApiParam::allowableValues).orElse(""));
		if (apiParam.isPresent()) {
			ApiParam annotation = apiParam.get();
			if (StringUtils.isNotEmpty(annotation.name())) {
				name = annotation.name();
			}
			String description = annotation.value();
			if (StringUtils.isEmpty(description)) {
				description = name;
			}
			context.parameterBuilder().name(name).description(descriptions.resolve(description))
					.parameterAccess(StringUtils.emptyOfNull(annotation.access()))
					.defaultValue(StringUtils.emptyOfNull(annotation.defaultValue()))
					.allowMultiple(annotation.allowMultiple()).allowEmptyValue(annotation.allowEmptyValue())
					.required(annotation.required()).scalarExample(new Example(annotation.example()))
					.complexExamples(examples(annotation.examples())).hidden(annotation.hidden())
					.collectionFormat(annotation.collectionFormat()).order(SWAGGER_PLUGIN_ORDER);
		}
	}

	private AllowableValues allowableValues(ResolvedType parameterType, String allowableValueString) {
		AllowableValues allowableValues = null;
		if (!isEmpty(allowableValueString)) {
			allowableValues = ApiModelProperties.allowableValueFromString(allowableValueString);
		} else {
			if (enumTypeDeterminer.isEnum(parameterType.getErasedType())) {
				allowableValues = Enums.allowableValues(parameterType.getErasedType());
			}
			if (Collections.isContainerType(parameterType)) {
				allowableValues = Enums
						.allowableValues(Collections.collectionElementType(parameterType).getErasedType());
			}
		}
		return allowableValues;
	}

	@Override
	public boolean supports(DocumentationType delimiter) {
		return pluginDoesApply(delimiter);
	}
}
